﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

namespace Nirvana
{
    [RequireComponent(typeof(FlowLayoutGroup))]
    [AddComponentMenu("Nirvana/UI/Layout/Rich Text Group")]
    public class RichTextGroup : MonoBehaviour
    {
        /// <summary>
        /// 下划线
        /// </summary>
        [Header("下划线")]
        [SerializeField]
        private bool isUnderline = false;
        [SerializeField]
        private float underlineHeight = 2f;
        [SerializeField]
        private Color32 underlineColor = Color.gray;
        /// <summary>
        /// 文本预制件
        /// </summary>
        [SerializeField]
        private Text textPrefab;
        private RectTransform rectTransform;
        private FlowLayoutGroup flowLayoutGroup;
        private List<GameObject> objlist = new List<GameObject>();

        public void SetTextPrefab(Text text)
        {
            textPrefab = text;

#if UNITY_EDITOR
            EditorUtility.SetDirty(gameObject);
#endif
        }

        public  void Clear()
        {
            foreach(var obj in this.objlist)
            {
                if(obj != null)
                {
                    obj.transform.SetParent(null, false);
                    if (Application.isPlaying) Destroy(obj);
#if UNITY_EDITOR
                    else
                        EditorApplication.delayCall += ()=> {
                        DestroyImmediate(obj);
                    };
#endif 
                }
            }
            
            this.objlist.Clear();
            if (this.flowLayoutGroup == null) this.flowLayoutGroup = GetComponent<FlowLayoutGroup>();
            this.flowLayoutGroup.LastRowWidth = 0f;
        }

        public void AddObject(GameObject obj)
        {
            if (this.rectTransform == null) this.rectTransform = GetComponent<RectTransform>();
            if (this.flowLayoutGroup == null) this.flowLayoutGroup = GetComponent<FlowLayoutGroup>();

            obj.transform.SetParent(this.flowLayoutGroup.transform, false);
            this.objlist.Add(obj);
        }

        public void AddNewLine()
        {
            if (this.rectTransform == null) this.rectTransform = GetComponent<RectTransform>();
            if (this.flowLayoutGroup == null) this.flowLayoutGroup = GetComponent<FlowLayoutGroup>();
            this.UpdateLayout();
            float width = this.flowLayoutGroup.ContentSizeFitterWidth;
            float lastWidth = this.flowLayoutGroup.LastRowWidth;
            float preWidth = width - lastWidth - 0.001f;
            preWidth = Mathf.Max(preWidth, 0);
            GameObject newline = null;
            if (isUnderline)
            {
                newline = new GameObject("NewLine", typeof(Image));
                newline.GetComponent<Image>().color = underlineColor;
            }
            else
            {
                newline = new GameObject("NewLine", typeof(RectTransform));
            }
           
            LayoutElement layoutElement = newline.AddComponent<LayoutElement>();
            layoutElement.preferredWidth = preWidth;
            layoutElement.preferredHeight = underlineHeight;
            newline.transform.SetParent(this.flowLayoutGroup.transform, false);
            this.objlist.Add(newline);
        }

        public void AddText(string text)
        {
            if (this.rectTransform == null) this.rectTransform = GetComponent<RectTransform>();
            if (this.flowLayoutGroup == null) this.flowLayoutGroup = GetComponent<FlowLayoutGroup>();
            this.UpdateLayout();
            Font font = this.textPrefab.font;
            font.RequestCharactersInTexture(text, this.textPrefab.fontSize, this.textPrefab.fontStyle);

            float workingRowWidth = this.flowLayoutGroup.ContentSizeFitterWidth;
            StringBuilder sb = new StringBuilder();
            float spaceX = this.flowLayoutGroup.Spacing.x;
            float lastWidth = this.flowLayoutGroup.LastRowWidth;
            string size = string.Empty;
            string color = string.Empty;
            int i = 0;
            var allChars = text.ToCharArray();
            while (i < text.Length)
            {
                char c = text[i];
                if (c != '<')
                {
                    if(c == '\n')
                    {
                        if (!string.IsNullOrEmpty(color))
                        {
                            sb.Append("</");
                            sb.Append(color);
                            sb.Append(">");
                        }
                        this.CreateTextObj(sb.ToString());
                        this.AddNewLine();
                        sb.Remove(0,sb.Length);
                        spaceX = 0;
                        lastWidth = 0;
                        if (!string.IsNullOrEmpty(size))
                        {
                            sb.Append("<");
                            sb.Append(size);
                            sb.Append(">");
                        }
                        i++;
                    }
                    else
                    {
                        CharacterInfo charInfo;
                        if(!font.GetCharacterInfo(c,out charInfo, this.textPrefab.fontSize, this.textPrefab.fontStyle))
                        {
                            Debug.LogWarningFormat("Can not get character info: {0}, {1}", c, font.fontNames);
                        }

                        float space = workingRowWidth - lastWidth;
                        if (Mathf.Approximately(space, 0))
                        {
                            space = workingRowWidth;
                            lastWidth = 0;
                        }
                        if(spaceX + charInfo.advance >= space)
                        {
                            if (!string.IsNullOrEmpty(color))
                            {
                                sb.Append("</");
                                sb.Append(color);
                                sb.Append(">");
                            }
                            this.CreateTextObj(sb.ToString());
                            this.AddNewLine();
                            sb.Remove(0, sb.Length);
                            spaceX = 0;
                            lastWidth = 0;
                            if (!string.IsNullOrEmpty(size))
                            {
                                sb.Append("<");
                                sb.Append(size);
                                sb.Append(">");
                            }
                        }

                        sb.Append(c);
                        spaceX += charInfo.advance;
                        i++;
                    }
                }
                else
                {
                    if (string.IsNullOrEmpty(size))
                    {
                        int index = text.IndexOf('>', i + 1);
                        if (index >= 0) size = text.Substring(i + 1, index - i - 1);
                        int index2 = size.IndexOf('=');
                        if (index2 >= 0) color = size.Substring(0, index2);
                        else color = size;
                        sb.Append("<");
                        sb.Append(size);
                        sb.Append(">");
                        i = index;

                    }
                    else
                    {
                        if(i+1>=text.Length ||  text[i+1] != '/')
                        {
                            if (c == '\n')
                            {
                                if (!string.IsNullOrEmpty(color))
                                {
                                    sb.Append("</");
                                    sb.Append(color);
                                    sb.Append(">");
                                }
                                this.CreateTextObj(sb.ToString());
                                this.AddNewLine();
                                sb.Remove(0, sb.Length);
                                spaceX = 0;
                                lastWidth = 0;
                                if (!string.IsNullOrEmpty(size))
                                {
                                    sb.Append("<");
                                    sb.Append(size);
                                    sb.Append(">");
                                }
                                i++;
                            }
                            else
                            {
                                CharacterInfo charInfo;
                                if (!font.GetCharacterInfo(c, out charInfo, this.textPrefab.fontSize, this.textPrefab.fontStyle))
                                {
                                    Debug.LogWarningFormat("Can not get character info: {0}, {1}", c, font.fontNames);
                                }

                                float space = workingRowWidth - lastWidth;
                                if (Mathf.Approximately(space, 0))
                                {
                                    space = workingRowWidth;
                                    lastWidth = 0;
                                }
                                if (spaceX + charInfo.advance >= space)
                                {
                                    if (!string.IsNullOrEmpty(color))
                                    {
                                        sb.Append("</");
                                        sb.Append(color);
                                        sb.Append(">");
                                    }
                                    this.CreateTextObj(sb.ToString());
                                    this.AddNewLine();
                                    sb.Remove(0, sb.Length);
                                    spaceX = 0;
                                    lastWidth = 0;
                                    if (!string.IsNullOrEmpty(size))
                                    {
                                        sb.Append("<");
                                        sb.Append(size);
                                        sb.Append(">");
                                    }
                                }

                                sb.Append(c);
                                spaceX += charInfo.advance;
                                i++;
                            }
                        }
                        else
                        {
                            int index = text.IndexOf('>', i + 1);
                            if (index >= 0) size = text.Substring(i + 2, index - i - 2);

                            sb.Append("</");
                            sb.Append(size);
                            sb.Append(">");
                            size = string.Empty;
                            color = string.Empty;
                            i = index;
                        }
                    }
                    i++;
                }
            }

            if (sb.Length > 0) this.CreateTextObj(sb.ToString());
        }

        private void CreateTextObj(string txt)
        {
            if (string.IsNullOrEmpty(txt)|| this.transform == null) return;
            Text text = Instantiate<Text>(this.textPrefab, this.transform, false);
            text.text = txt;
            text.horizontalOverflow = HorizontalWrapMode.Overflow;
            text.verticalOverflow = VerticalWrapMode.Overflow;
            text.transform.SetParent(this.flowLayoutGroup.transform, false);
            this.objlist.Add(text.gameObject);
            text.gameObject.SetActive(true);
        }

        private void UpdateLayout()
        {
            if (this.flowLayoutGroup)
            {
                this.flowLayoutGroup.UpdateLayoutHorizontal();
                this.flowLayoutGroup.UpdateLayoutVertical();
            }
        }

        
    }
}

